package main

import (
	"bufio"
	"crypto/ecdsa"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"time"

	"gitee.com/thubcc/blockchain/p2p"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/log"
	ethp2p "github.com/ethereum/go-ethereum/p2p"
	"github.com/ethereum/go-ethereum/p2p/enode"
	"github.com/ethereum/go-ethereum/params"
)

var (
	logRec     = make(map[string]string)
	currentRec = make(map[string]string)
	nodes      = make([]*ethp2p.Server, 0)
	argNum     = flag.Int("n", 2, "start node")
	argFile    = flag.String("f", ".ethereum/node", "node json")
	fileCount  = 0
	start      = time.Now()
)

type record struct {
	Addr string `json:"addr"`
	ID   string `json:"id"`
}

func processIp(i int, p *record) {
	if i%1000 == 999 {
		fmt.Printf("*")
	}
	laddr, lok := logRec[p.ID]
	caddr, cok := currentRec[p.ID]
	if (lok && laddr != "") || (cok && caddr != "") {
		return
	}
	if p.ID == "" || p.Addr == "" {
		return
	}
	currentRec[p.ID] = p.Addr
	if len(currentRec) >= 1024 || time.Since(start).Minutes() > 15. {
		dumpToFile(fileCount)
		fileCount++
		start = time.Now()
		fmt.Println()
		fmt.Printf("%7d %s %s\n", i, p.ID, p.Addr)
	}

}

func LogProcessor(r *io.PipeReader, restart chan struct{}) {
	defer close(restart)
	reader := bufio.NewReader(r)
	for i := 0; ; i++ {
		line, err := reader.ReadString('\n')
		if err != nil {
			fmt.Println(err)
			return
		}
		var p = record{}
		err = json.Unmarshal([]byte(line), &p)
		if err == nil {
			processIp(i, &p)
		}
	}
}

func GenKey(match, len int) *ecdsa.PrivateKey {
	for {
		asymKey, err := crypto.GenerateKey()
		if err != nil {
			fmt.Printf("gen node kry: %v\n", err)
		}
		id := enode.PubkeyToIDV4(&asymKey.PublicKey)
		var hi = int(id[0])
		mask := 1 << len
		mask--
		if (hi>>(8-len))&mask == match {
			return asymKey
		}
	}
}

func Log2(i int) int {
	r := 0
	for {
		if i == 0 {
			return r
		}
		i = i >> 1
		r++
	}
}

func dumpToFile(i int) {
	buf, err := json.MarshalIndent(currentRec, "", "  ")
	if err != nil {
		fmt.Println("Dump to file error")
	}
	fn := fmt.Sprintf("%s%05d.json", *argFile, i)

	if err = ioutil.WriteFile(fn, buf, 0755); err != nil {
		fmt.Println("write to file error")
	}
	for k, v := range currentRec {
		logRec[k] = v
	}
	currentRec = make(map[string]string)
}

func prepareNode(n int) {
	var peers = []*enode.Node{}
	for _, mainnode := range params.MainnetBootnodes {
		peer := enode.MustParse(mainnode)
		peers = append(peers, peer)
	}

	len := Log2(n - 1)
	for i := 0; i < n; i++ {
		key := GenKey(i, len)
		server, err := p2p.NewNullNode(peers, key)
		if err != nil {
			fmt.Println("init p2p failure", err)
		} else {
			fmt.Println("Setup p2p Server")
		}
		nodes = append(nodes, server)
	}
}

func main() {
	flag.Parse()
	r, w := io.Pipe()
	logHandler := log.StreamHandler(w, log.JSONFormat())
	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(6), logHandler))
	restart := make(chan struct{})
	go LogProcessor(r, restart)

	prepareNode(*argNum)
	for _, v := range nodes {
		v.Start()
	}

	defer func() {
		for _, v := range nodes {
			v.Stop()
		}
	}()
	<-restart
}
